package ytmc.converter;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import ytmc.setup.*;
import ytmc.FileSize;
import ytmc.TimePeriod;
import ytmc.converter.ffmpeg.FFmpegConverter;


/**
 *
 * @author Kicok
 */
public class ConverterModel extends AbstractTableModel
{
    private String[] columnNames = { "Plik wejściowy", "Rodzaj zadania", "Plik wyjściowy", "Status", "Rozmiar", "Postęp", "Pozostały czas" };
    private Class[] columnClasses = { String.class, String.class, String.class, String.class, FileSize.class, String.class, String.class };
    private ArrayList<ConversionTask> tasks;

    public ConverterModel()
    {
        this.tasks = new ArrayList<ConversionTask>();

        loadTasks();
    }


    public List<ConversionTask> getTasks()
    {
        return tasks;
    }

    public ConversionTask createTask( String inputFilePath, String outputFilePath )
    {
        return createTask( new File( inputFilePath ), new File( outputFilePath ) );
    }

    public ConversionTask createTask( File inputFile, File outputFile )
    {
        XMLConfiguration config = Config.load();

        ConversionTask task = new ConversionTask( inputFile, outputFile );
        task.setVideoFormat( ConfigUtils.getEnum( config, "video-format", VideoFormat.DEFAULT_FORMAT ) );
        task.setVideoResolution( ConfigUtils.getEnum( config, "video-resolution", VideoResolution.DEFAULT_RESOLUTION ) );
        task.setVideoFrameRate( config.getInt( "video-frame-rate", 0 ) );
        task.setAudioFormat( ConfigUtils.getEnum( config, "audio-format", AudioFormat.DEFAULT_FORMAT ) );
        task.setAudioQuality( ConfigUtils.getEnum( config, "audio-quality", AudioQuality.DEFAULT_QUALITY ) );

        addTask( task );
        return task;
    }

    public void addTask( ConversionTask task )
    {
        tasks.add( task );
        saveTasks();
        int number = tasks.size() - 1;

        fireTableRowsInserted( number, number );
    }

    public void removeTask( int i )
    {
        if( i >= 0 && i < tasks.size() )
        {
            tasks.remove( i );
            saveTasks();

            fireTableRowsDeleted( i, i );
        }
    }

    public void startTask( int i ) throws Exception
    {
        ConversionTask task = tasks.get( i );

        try
        {
            new ConversionTaskValidator( task ).validate();

            Converter converter = new FFmpegConverter( task, i );
            converter.addObserver( new Observer() {

                @Override
                public void update( Observable o, Object o1 )
                {
                    if( o instanceof Converter && o1 instanceof Integer )
                    {
                        int id = ( (Integer)o1 ).intValue();
                        fireTableRowsUpdated( id, id );

                        if( ( (Converter)o ).getTask().getStatus() == ConversionStatus.FINISHED )
                        {
                            saveTasks();
                        }
                    }
                }
            });

            converter.convertAsync();
        }
        catch( Exception e )
        {
            task.setProgress( -1 );
            task.setRemainingTime( -1 );
            task.setStatus( ConversionStatus.FAILED );
            fireTableRowsUpdated( i, i );

            throw e;
        }
    }


    private void loadTasks()
    {
        tasks.clear();
        XMLConfiguration config = Config.load();

        try
        {
            List savedTasks = config.getList( "conversion.tasks.task.input-file-path" );
            if( savedTasks != null )
            {
                for( int i = 0; i < savedTasks.size(); i++ )
                {
                    File inputFile = new File( config.getString( "conversion.tasks.task{" + i + "}.input-file-path" ) );
                    File outputFile = new File( config.getString( "conversion.tasks.task{" + i + "}.output-file-path" ) );
                    ConversionStatus status = ConfigUtils.getEnum( config, "conversion.tasks.task{" + i + "}.status", ConversionStatus.NOT_STARTED );

                    ConversionTask task = new ConversionTask( inputFile, outputFile );
                    task.setVideoFormat( ConfigUtils.getEnum( config, "conversion.tasks.task{" + i + "}.video-format", VideoFormat.DEFAULT_FORMAT ) );
                    task.setVideoResolution( ConfigUtils.getEnum( config, "conversion.tasks.task{" + i + "}.video-resolution", VideoResolution.DEFAULT_RESOLUTION ) );
                    task.setVideoFrameRate( config.getInt( "conversion.tasks.task{" + i + "}.video-frame-rate", 0 ) );
                    task.setAudioFormat( ConfigUtils.getEnum( config, "conversion.tasks.task{" + i + "}.audio-format", AudioFormat.DEFAULT_FORMAT ) );
                    task.setAudioQuality( ConfigUtils.getEnum( config, "conversion.tasks.task{" + i + "}.audio-quality", AudioQuality.DEFAULT_QUALITY ) );
                    task.setStatus( status );

                    tasks.add( task );
                }
            }
        }
        catch( Exception e)
        {
            System.out.println( "Błąd podczas wczytywania listy zadań konwersji" );
            System.out.println( e );
        }
    }

    private void saveTasks()
    {
        XMLConfiguration config = Config.load();

        try
        {
            ConfigUtils.removeProperty( config, "conversion.tasks" );

            int i = 0;
            for( ConversionTask task : tasks )
            {
                config.setProperty( "conversion.tasks.task{" + i + "}.input-file-path", task.getInputFile().getAbsolutePath() );
                config.setProperty( "conversion.tasks.task{" + i + "}.output-file-path", task.getOutputFile().getAbsolutePath() );
                config.setProperty( "conversion.tasks.task{" + i + "}.video-format", task.getVideoFormat().toString() );
                config.setProperty( "conversion.tasks.task{" + i + "}.video-resolution", task.getVideoResolution().toString() );
                config.setProperty( "conversion.tasks.task{" + i + "}.video-frame-rate", task.getVideoFrameRate() );
                config.setProperty( "conversion.tasks.task{" + i + "}.audio-format", task.getAudioFormat().toString() );
                config.setProperty( "conversion.tasks.task{" + i + "}.audio-quality", task.getAudioQuality().toString() );
                ConversionStatus status = task.getStatus();
                if( status == ConversionStatus.RUNNING )
                {
                    status = ConversionStatus.NOT_STARTED;
                }
                config.setProperty( "conversion.tasks.task{" + i + "}.status", status.toString() );

                i++;
            }

            config.save();
        }
        catch( ConfigurationException e )
        {
            System.out.println( "Błąd podczas zapisywania listy zadań konwersji" );
            System.out.println( e );
        }
    }


    @Override
    public Class<?> getColumnClass( int i )
    {
        return columnClasses[i];
    }

    @Override
    public int getColumnCount()
    {
        return columnNames.length;
    }

    @Override
    public String getColumnName( int i )
    {
        return columnNames[i];
    }

    @Override
    public int getRowCount()
    {
        return tasks.size();
    }

    @Override
    public Object getValueAt( int i, int i1 )
    {
        if( i < 0 || i >= getRowCount() )
        {
            return null;
        }

        switch( i1 )
        {
            case 0: return tasks.get( i ).getInputFile().getName();
            case 1: return tasks.get( i ).getType().getFriendlyName();
            case 2: return tasks.get( i ).getOutputFile().getName();
            case 3: return tasks.get( i ).getStatus().getFriendlyName();
            case 4: return tasks.get( i ).getInputFileSize();
            case 5:
            {
                int progress = tasks.get( i ).getProgress();
                if( progress >= 0 )
                {
                    return String.format( "%d %%", progress );
                }

                return "";
            }
            case 6:
            {
                int secondsRemaining = tasks.get( i ).getRemainingTime();
                if( secondsRemaining >= 0 )
                {
                    return new TimePeriod( secondsRemaining );
                }

                return "";
            }

            default: return null;
        }
    }

    @Override
    public boolean isCellEditable( int i, int i1 )
    {
        return false;
    }

    @Override
    public void setValueAt( Object o, int i, int i1 )
    {
        // Table is read only
    }
}